/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __VECTOR_H__
#define __VECTOR_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "StdAfx.h"
#include <math.h>
#include <assert.h>
#include "Matrix.h"

namespace math {

class Matrix4;
class Vector4; 

#define vec_t float
#define INF 999999.9f

/**
 * A 2D Vector Class<br>
 * This class uses templates.<br>
 * See typedefs after class definition.
 */
template <class T>
class DLL_EXPORT Vector2
{
public:
	static Vector2 UNDEFINED;

	/// The components of the vector
	T x, y;

	/// vector constructor
	Vector2(T a=0, T b=0) : x(a), y(b) {};

	/// vector copy constructor
	Vector2(const Vector2<T> &vec) : x(vec.x), y(vec.y) {};

	Vector2(const float *v) : x(v[0]), y(v[1]) {};

	/// returns the length of the vector
	const vec_t length() const
	{
		return (vec_t)sqrt(x*x + y*y);
	}

	const vec_t lengthSquared() const
	{
		return (x*x + y*y);
	}

	/// vector assignment
	const Vector2<T> &operator=(const Vector2<T> &vec)
	{
		x=vec.x;
		y=vec.y;
		return *this;
	}

	/// vector equality
	const bool operator==(const Vector2<T> &vec) const
	{
		return ((x == vec.x) && (y == vec.y));
	}

	/// vector less
	const bool operator<(const Vector2<T> &vec) const
	{
		return ((x < vec.x) || (y < vec.y));
	}

	/// vector greater
	const bool operator>(const Vector2<T> &vec) const
	{
		return ((x > vec.x) || (y > vec.y));
	}

	/// vector inequality
	const bool operator!=(const Vector2<T> &vec) const
	{
		return !(*this == vec);
	}

	/// vector add
	const Vector2<T> operator+(const Vector2<T> &vec) const
	{
		return Vector2<T>(x + vec.x, y + vec.y);
	}

	/** vector add (opposite of negation) */
	const Vector2<T> operator+() const
	{
		return Vector2<T>(*this);
	}

	/** vector increment */
	const Vector2<T>& operator+=(const Vector2<T> &vec)
	{
		x += vec.x;
		y += vec.y;
		return *this;
	}

	/** vector subtraction */
	const Vector2<T> operator-(const Vector2<T> &vec) const
	{
		return Vector2<T>(x - vec.x, y - vec.y);
	}

	/** vector negation */
	const Vector2<T> operator-() const
	{
		return Vector2<T>(-x, -y);
	}

	/** vector decrement */
	const Vector2<T> &operator-=(const Vector2<T> &vec)
	{
		x -= vec.x;
		y -= vec.y;
		return *this;
	}

	/** scalar self-multiply */
	const Vector2 &operator*=(const vec_t &s)
	{
		x *= s;
		y *= s;
		return *this;
	}

	/** scalar self-divide */
	const Vector2 &operator/=(const vec_t &s)
	{
		assert( s != 0 );
		const vec_t recip = 1/s;
		x *= recip;
		y *= recip;
		return *this;
	}

	/** post multiply by scalar */
	const Vector2<T> operator*(const vec_t &s) const
	{
		return Vector2(x*s, y*s);
	}

	/** pre multiply by scalar */
	friend inline const Vector2<T> operator*(const vec_t &s, const Vector2<T> &vec)
	{
		return vec*s;
	}

	/** divide by scalar */
	const Vector2<T> operator/(vec_t s) const
	{
		assert( s != 0 );
		s = 1/s;
		return Vector2(static_cast<T>(s*x), static_cast<T>(s*y));
	}

	/** dot product */
	const vec_t operator%(const Vector2<T> &vec) const
	{
		return x*vec.x + y*vec.y;
	}

	/** length of vector */
	const vec_t operator!() const
	{
		return sqrt(x*x + y*y);
	}

	/** return vector with specified length */
	const Vector2<T> operator | (const vec_t length) const
	{
		return *this * (length / !(*this));
	}

	/** set length of vector equal to length */
	const Vector2<T>& operator |= (const vec_t length)
	{
		return *this = *this | length;
	}

	/** Normalizes the vector, i.e. set the length to 1 */
	void normalize()
	{
		if (length() != 0)
			(*this) /= length();
	}

	/** Returns the distance to another vector */
	const vec_t distance(const Vector2<T> &vec)
	{
		return ((vec_t) sqrt( (vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) ) );
	}

	/// Returns the angle between this and another vector
	const vec_t   angle(const Vector2 &normal) const;

	/** Returns the squared distance to another vector */
	const vec_t sqrDistance(const Vector2<T> &vec)
	{
		return (vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y);
	}

	const vec_t dotProduct(const Vector2 &vec) const
	{
		return (x*vec.x + y*vec.y);
	}

	/** Prints the components of the vector to std::cout */
	void print() const
	{
		STRINGCOUT(x << " " << y);
	}
};

// return angle between two vectors
template <class T>
const vec_t Vector2<T>::angle(const Vector2<T>& normal) const
{
	return acos(*this % normal);
}

/*************************************************
 *                                               *
 *   Type definitions                            *
 *                                               *
 *************************************************/

/// A 2D Vector using float values
typedef Vector2<float>  Vector2f;
/// A 2D Vector using int values
typedef Vector2<int>    Vector2i;

/**
 * The Vector3 class.<br>
 * A three dimensional float vector.
 */
class DLL_EXPORT Vector3
{
public:
	
	/** The components of the vector */
	vec_t x, y, z;    // x,y,z coordinates
	
	static Vector3 UNDEFINED;	

	/** vector constructor */
	Vector3(vec_t a = 0.0, vec_t b = 0.0, vec_t c = 0.0) : x(a), y(b), z(c) {};

	/** vector copy constructor */
	Vector3(const Vector3 &vec) : x(vec.x), y(vec.y), z(vec.z) {};

	Vector3(const float *v) : x(v[0]), y(v[1]), z(v[2]) {};

	inline vec_t& operator[](unsigned int id)
	{
		assert(id <3);
		return (&x)[id];
	}

	const vec_t& get(unsigned int i) const
	{
		return (&x)[i];
	}

	/** vector assignment */
	const Vector3& operator=(const Vector3 &vec)
	{
		x=vec.x;
		y=vec.y;
		z=vec.z;
		return *this;
	}

	const Vector3 &operator=(const Vector4 &vec);

	/** vector equality */
	const bool operator==(const Vector3 &vec) const
	{
		return ((x == vec.x) && (y == vec.y) && (z == vec.z));
	}

	const bool operator<(const Vector3 &vec) const
	{
		return ((x < vec.x) || (y < vec.y) || (z < vec.z));
	}

	const bool operator>(const Vector3 &vec) const
	{
		return ((x > vec.x) || (y > vec.y) || (z > vec.z));
	}
	/** vector inequality */
	const bool operator!=(const Vector3 &vec) const
	{
		return !(*this == vec);
	}

	/** vector add */
	const Vector3 operator+(const Vector3 &vec) const
	{
		return Vector3(x + vec.x, y + vec.y, z + vec.z);
	}

	/** vector add (opposite of negation) */
	const Vector3 operator+() const
	{
		return Vector3(*this);
	}

	/** vector increment */
	const Vector3& operator+=(const Vector3 &vec)
	{
		x += vec.x;
		y += vec.y;
		z += vec.z;
		return *this;
	}

	/** vector subtraction */
	const Vector3 operator-(const Vector3 &vec) const
	{
		return Vector3(x - vec.x, y - vec.y, z - vec.z);
	}

	/** vector negation */
	const Vector3 operator-() const
	{
		return Vector3(-x, -y, -z);
	}

	/** vector decrement */
	const Vector3 &operator-=(const Vector3 &vec)
	{
		x -= vec.x;
		y -= vec.y;
		z -= vec.z;
		return *this;
	}

	/** scalar self-multiply */
	const Vector3 &operator*=(const vec_t &s)
	{
		x *= s;
		y *= s;
		z *= s;
		return *this;
	}

	/** scalar self-divide */
	const Vector3 &operator/=(const vec_t &s)
	{
		assert( s != 0 );
		const vec_t recip = 1/s; // for speed, one divecision
		x *= recip;
		y *= recip;
		z *= recip;
		return *this;
	}

	/** post multiply by scalar */
	const Vector3 operator*(const vec_t &s) const
	{
		return Vector3(x*s, y*s, z*s);
	}

	/** pre multiply by scalar */
	friend inline const Vector3 operator*(const vec_t &s, const Vector3 &vec)
	{
		return vec*s;
	}

	Vector3 operator*(const math::Matrix4 &mat) const;

	/** divide by scalar */
	const Vector3 operator/(vec_t s) const
	{
		assert( s != 0 );
		s = 1/s;
		return Vector3(s*x, s*y, s*z);
	}

	const Vector3 operator/(const Vector3 &vec) const 
	{
		return Vector3( x/vec.x, y/vec.y, z/vec.z);
	}

	const Vector3 operator*(const Vector3 &vec) const 
	{
		return Vector3( x*vec.x, y*vec.y, z*vec.z);
	}

	/** cross product */
	const Vector3 operator^(const Vector3 &vec) const
	{
		return Vector3(y*vec.z - z*vec.y, z*vec.x - x*vec.z, x*vec.y - y*vec.x);
	}

	/** dot product */
	const vec_t operator%(const Vector3 &vec) const
	{
		return x*vec.x + y*vec.y + z*vec.z;
	}

	/** length of vector */
	const vec_t operator!() const
	{
		return sqrt(x*x + y*y + z*z);
	}

	/** return vector with specified length */
	const Vector3 operator | (const vec_t length) const
	{
		return *this * (length / !(*this));
	}

	/// Set length of vector equal to length
	const Vector3& operator |= (const vec_t length)
	{
		return *this = *this | length;
	}

	/// Get the dot product of the vector and another vector
	const vec_t   dotProduct(const Vector3 &vec) const;
  
	/// Calculates the cross product
	const Vector3 crossProduct(const Vector3 &vec) const;

	/// Get the length of the vector
	const vec_t   length() const;
	
	/// Get the squared length of the vector
	const vec_t   lengthSquared() const;	

	/// Return the distance to another vector
	const vec_t   distance(const Vector3 &vec) const;

	/// Returns the square distance to another vector
	const vec_t	  sqrDistance(const Vector3 &vec) const;

	/// Returns the angle between this and another vector
	const vec_t   angle(const Vector3 &normal) const;

	/// Calculates a new vector with the unit length
	const Vector3 unitVector() const;

	/// Returns the reflection vector with a given normal
	const Vector3 reflection(const Vector3 &normal) const;

	/// Calculates a linear interpolation at time l between vec1 and vec2
	const Vector3 lerp(const Vector3 &vec1, const Vector3 &vec2, const float l);

	/// Normalizes this vector
	void          normalize();

	/// Set the length to 0
	void          setZero();

	/// Calculates the bicubic interpolation
	void          setBicubicInterpolation(const Vector3 &A, const Vector3 &TA, const Vector3 &TB, const Vector3 &B, vec_t t);

	/// Calculate and set this vector as the normal vector of the vectors A, B, and C
	void          setAsNormal(const Vector3& A, const Vector3 &B, const Vector3 &C);

	/// Set the vector values
	void          set(vec_t x, vec_t y, vec_t z);
	
	void          transformBy(const Vector3 &position, float *m);
	static void   normCrossProduct(const Vector3 &v1, const Vector3 &v2, Vector3 &out);
	const Vector3 getDistanceVectorFromLine(const Vector3 &pointOnLine, const Vector3 &direction);
	const Vector3 floor(const Vector3 &v);
	const Vector3 ceil(const Vector3 &v);

	void          print() const;
};


/**
 * The Vector4 class.<br>
 * A four dimensional float vector.
 */
class DLL_EXPORT Vector4
{
public:
	static Vector4 UNDEFINED;

	/** The components of the vector */
	vec_t x, y, z, w;    // x,y,z,w coordinates

	Vector4(vec_t a = 0.0, vec_t b = 0.0, vec_t c = 0.0, vec_t d = 0.0);
	Vector4(const Vector4 &vec);
	Vector4(const Vector3 &vec, vec_t d = 0.0);
	Vector4(const float *v) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {};

	/** vector assignment */
	const Vector4 &operator=(const Vector4 &vec)
	{
		x=vec.x;
		y=vec.y;
		z=vec.z;
		w=vec.w;
		return *this;
	}

	/** vector equality */
	const bool operator==(const Vector4 &vec) const
	{
		return ((x == vec.x) && (y == vec.y) && (z == vec.z) && (w == vec.w));
	}

	/// Array operator
	inline vec_t& operator[](unsigned int id)
	{
		assert(id < 4);
		return (&x)[id];
	}

	const bool operator<(const Vector4 &vec) const
	{
		return ((x < vec.x) || (y < vec.y) || (z < vec.z) || (w == vec.w));
	}

	const bool operator>(const Vector4 &vec) const
	{
		return ((x > vec.x) || (y > vec.y) || (z > vec.z) || (w == vec.w));
	}

	/** vector inequality */
	const bool operator!=(const Vector4 &vec) const
	{
		return !(*this == vec);
	}

	/** vector add */
	const Vector4 operator+(const Vector4 &vec) const
	{
		return Vector4(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
	}

	/** vector add (opposite of negation) */
	const Vector4 operator+() const
	{
		return Vector4(*this);
	}

	/** vector increment */
	const Vector4& operator+=(const Vector4 &vec)
	{
		x += vec.x;
		y += vec.y;
		z += vec.z;
		w += vec.w;
		return *this;
	}

	/** vector subtraction */
	const Vector4 operator-(const Vector4 &vec) const
	{
		return Vector4(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
	}

	/** vector negation */
	const Vector4 operator-() const
	{
		return Vector4(-x, -y, -z, -w);
	}

	/** vector decrement */
	const Vector4 &operator-=(const Vector4 &vec)
	{
		x -= vec.x;
		y -= vec.y;
		z -= vec.z;
		w -= vec.w;
		return *this;
	}

	/** scalar self-multiply */
	const Vector4 &operator*=(const vec_t &s)
	{
		x *= s;
		y *= s;
		z *= s;
		w *= s;
		return *this;
	}

	/** scalar self-divide */
	const Vector4 &operator/=(const vec_t &s)
	{
		assert( s != 0 );
		const vec_t recip = 1/s; // for speed, one divecision
		x *= recip;
		y *= recip;
		z *= recip;
		w *= recip;
		return *this;
	}	

	/** post multiply by scalar */
	const Vector4 operator*(const vec_t &s) const
	{
		return Vector4(x*s, y*s, z*s, w*s);
	}

	/** pre multiply by scalar */
	friend inline const Vector4 operator*(const vec_t &s, const Vector4 &vec)
	{
		return vec*s;
	}

	/** divide by scalar */
	const Vector4 operator/(vec_t s) const
	{
		assert( s != 0 );
		s = 1/s;
		return Vector4(s*x, s*y, s*z, s*w);
	}

	/** dot product */
	const vec_t operator%(const Vector4 &vec) const
	{
		return x*vec.x + y*vec.y + z*vec.z + w*vec.w;
	}

	/** Length of vector */
	const vec_t operator!() const
	{
		return sqrt(x*x + y*y + z*z + w*w);
	}

	/** Return vector with specified length */
	const Vector4 operator | (const vec_t length) const
	{
		return *this * (length / !(*this));
	}

	/** Set length of vector equal to length */
	const Vector4& operator |= (const vec_t length)
	{
		return *this = *this | length;
	}

	/// Calculate the dot product of this vector with another 4D vector and return it
	const vec_t   dotProduct(const Vector4 &vec) const;

	/// Calculate the cross product
	const Vector4 crossProduct(const Vector4 &vec) const;

	/// Return the length of the vector
	const vec_t   length() const;

	const vec_t   lengthSquared() const;

	/// Return the distance from this vector (interpreted as a point) to another point in space
	const vec_t   distance(const Vector4 &vec);

	/// Returns the angle between two vectors
	const vec_t   angle(const Vector4 &normal) const;

	/// Makes this vector a unit vector
	const Vector4 unitVector() const;

	/// Reflects this vector at a given normal
	const Vector4 reflection(const Vector4 &normal) const;

	/// Performs a linear interpolation between vec1 and vec2 at a given l [0..1]
	const Vector4 lerp(const Vector4 &vec1, const Vector4 &vec2, const float l);

	/// Normalize the vector
	void          normalize();

	/// Set all the vector components to zero
	void		  setZero();

	/// Multiplies the vector with a matrix
	Vector4 operator*(const math::Matrix4 &mat) const;

	/// Print the vector components to std::cout
	void		  print() const;
};

} // namespace

#endif
